package org.sws.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.CuratorCache;
import org.apache.curator.framework.recipes.cache.CuratorCacheListener;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.sws.types.annotations.DCCValue;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;


/**
 * @Author sws
 * @Date 2025/4/17 17:19
 * @description: 基于zookeeper的动态配置中心
 */
@Slf4j
public class DCCValueBeanFactory implements BeanPostProcessor {
    /* zookeeper中节点路径 */
    private static final String BASE_CONFIG_PATH_CONFIG = "/big-market-dcc/config";

    private CuratorFramework client;
    private final Map<String, Object> dccObjGroup = new HashMap<>();

    public DCCValueBeanFactory(CuratorFramework client) throws Exception {
        if (null == client) {
            return;
        }
        this.client = client;

        if(null == client.checkExists().forPath(BASE_CONFIG_PATH_CONFIG)){
            // 节点不存在，创建base节点
            client.create().creatingParentsIfNeeded().forPath(BASE_CONFIG_PATH_CONFIG);
            log.info("DCC 节点监听 base node 「{}」 not absent create new done!", BASE_CONFIG_PATH_CONFIG);
        }

        CuratorCache curatorCache = CuratorCache.build(client, BASE_CONFIG_PATH_CONFIG);
        curatorCache.start();

        curatorCache.listenable().addListener((type, oldData, data) -> {
            if (Objects.requireNonNull(type) == CuratorCacheListener.Type.NODE_CHANGED) {
                String dccValuePath = data.getPath();
                Object objBean = dccObjGroup.get(dccValuePath);
                if (null == objBean) {
                    return;
                }
                try {
                    // 1. getDeclaredField 方法用于获取指定类中声明的所有字段，包括私有字段、受保护字段和公共字段。
                    // 2. getField 方法用于获取指定类中的公共字段，即只能获取到公共访问修饰符（public）的字段。
                    Field field = objBean.getClass().getDeclaredField(dccValuePath.substring(dccValuePath.lastIndexOf("/") + 1));
                    field.setAccessible(true);
                    field.set(objBean, new String(data.getData()));
                    field.setAccessible(false);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    /**
     * 基于注解进行获取配置值，实现spring工厂提供的BeanPostProcessor接口，在spring容器对象初始化后，对 @DCCValue 注解的字段进行动态配置管理
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (null == client){
            return bean;
        }
        // 增加 AOP 代理后，获得类的方式要通过 AopProxyUtils.getTargetClass(bean); 不能直接 bean.class 因为代理后类的结构发生变化，这样不能获得到自己的自定义注解了。
        Class<?> targetBeanClass = bean.getClass();
        Object targetBeanObject = bean;
        if (AopUtils.isAopProxy(bean)) {
            targetBeanClass = AopUtils.getTargetClass(bean);
            targetBeanObject = AopProxyUtils.getSingletonTarget(bean);
        }
        // 获取当前类中所有字段，包括私有字段、受保护字段和公共字段
        Field[] fields = targetBeanClass.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(DCCValue.class)) {
                continue;
            }

            DCCValue dccValue = field.getAnnotation(DCCValue.class);

            String value = dccValue.value();
            if (StringUtils.isBlank(value)) {
                // 添加注解但是没有配置值，则抛出异常
                throw new RuntimeException(field.getName() + " @DCCValue is not config value config case 「isSwitch/isSwitch:1」");
            }

            String[] splits = value.split(":");
            String key = splits[0];
            String defaultValue = splits.length == 2 ? splits[1] : null;

            String keyPath = BASE_CONFIG_PATH_CONFIG.concat("/").concat(key);
            try {
                // 判断当前节点是否存在，不存在则创建出 Zookeeper 节点，将注解配置的value设置给对应属性字段；zookeeper中存在节点则直接获取节点值，设置给对应属性字段
                if (null == client.checkExists().forPath(keyPath)) {
                    client.create().creatingParentsIfNeeded().forPath(keyPath);
                    if (StringUtils.isNotBlank(defaultValue)) {
                        client.setData().forPath(keyPath, defaultValue.getBytes(StandardCharsets.UTF_8));
                        field.setAccessible(true);
                        field.set(targetBeanObject, defaultValue);
                        field.setAccessible(false);
                    }
                    log.info("DCC 节点监听 创建节点 {}", keyPath);
                } else {
                    String configValue = new String(client.getData().forPath(keyPath));
                    if (StringUtils.isNotBlank(configValue)) {
                        field.setAccessible(true);
                        field.set(targetBeanObject, configValue);
                        field.setAccessible(false);
                        log.info("DCC 节点监听 设置配置 {} {} {}", keyPath, field.getName(), configValue);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            dccObjGroup.put(keyPath, targetBeanObject);
        }
        return bean;
    }
}
